package kr.ac.konkuk.db.quadtree.impl;

import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;

import kr.ac.konkuk.db.quadtree.QuadTree;
import kr.ac.konkuk.db.quadtree.RowKey;
import kr.ac.konkuk.db.quadtree.callback.GeometryProvider;
import kr.ac.konkuk.db.quadtree.callback.SearchOperator;
import kr.ac.konkuk.db.quadtree.node.LeafNode;
import kr.ac.konkuk.db.quadtree.node.NodeBase;
import kr.ac.konkuk.db.quadtree.node.NodeVisitor;
import kr.ac.konkuk.db.quadtree.node.Root;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;

public class QuadTreeImpl extends AbstractQuadTree<Geometry> {
	
	public QuadTreeImpl(Envelope env, GeometryProvider provider) {
		super(new Root(env, provider));
	}

	public Set<String> insert(RowKey rowKey, Geometry item) {
		Set<String> cfSet = new HashSet<String>();
		for (Coordinate coord : item.getCoordinates()) {
			root.put(rowKey, coord, cfSet);
		}
		return cfSet;
	}
	
	public Set<String> insert(List<Byte> rowKey, Geometry item) {
		return this.insert(new RowKey(rowKey), item);
	}

	public Set<String> search(Envelope searchEnv, SearchOperator operator) {
		final Set<String> cfSet = new HashSet<String>();
		root.search(searchEnv, new NodeVisitor() {
			public boolean visit(NodeBase node) {
				cfSet.add(node.columnFamily());
				
				return true;
			}
		});
		
		return cfSet;
	}

	public boolean remove(RowKey rowKey) {
		return root.remove(rowKey);
	}

	public Root getRoot() {
		return root; 
	}

	public boolean remove(List<Byte> rowKey) {
		return remove(new RowKey(rowKey));
	}
	
	public String toString() {
		return root.toString(0);
	}

	@Override
	public Set<String> search(final Geometry searchEnv, final SearchOperator operator) {
		final Set<String> cfSet = new HashSet<String>();
		root.visits(new NodeVisitor() {
			public boolean visit(final NodeBase node) {
				if (node instanceof LeafNode) {
					LeafNode node1 = (LeafNode)node;
					if (operator.operate(searchEnv, node1.env())) {
						cfSet.add(node.columnFamily());
					}
				}
				return true;
			}
		});
		
		return cfSet;
	}
}
